<?php

namespace App\Admin\Console\Commands;

use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Artisan;
use App\Admin\Models\VueRouter;
use App\Admin\Models\VueAdminUserRouter;
use App\Admin\Models\SystemModel;
use App\Admin\Models\AdminUser;
use App\Admin\Models\AdminPermission;

class ResourceMakeCommand extends GeneratorCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = '
        admin:make-resource
        {slug : 数据表名称}
        {--force : 强制覆盖}
        {--model= : 指定模型}
        {--test : 生成控制器测试类}
    ';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '添加一个后台资源，包含各种相关文件';

    protected $types = [
        'model', 'filter', 'request', 'resource', 'controller', 'showdoc', 'test',
    ];

    /**
     * 当前正在生成的类型
     *
     * @var string
     */
    protected $nowType;

    /**
     * 当前正在生成的数据库表名
     *
     * @var string
     */
    protected $title;

    /**
     * 各类型对应的完整的类名
     *
     * @var array
     */
    protected $classes = [];

    protected $frontendTypePathMap = [
        'api' => 'api/dummy-resources.js',
        'index' => 'views/dummy-resources/Index.vue',
        'form' => 'views/dummy-resources/Form.vue',
        'detail' => 'views/dummy-resources/Detail.vue',
    ];

    /**
     * Execute the console command.
     */
    public function handle()
    {   
        if (config()->get('app.env')=='production') {
            $this->warn('生产环境下不适用');
            return false;
        }
        $slug = $this->argument('slug');
        if (!Schema::hasTable($slug)) {
            $this->error("不存在到数据库表 [ {$slug} ]");
            return false;
        }
        // 判断数据表名称是否为复数
        $pluralKebabDummyResource = Str::plural(Str::kebab($slug));
        if ($pluralKebabDummyResource!=$slug) {
            $this->error("请把数据表名称 [ {$slug} ] 更改为 [ {$pluralKebabDummyResource} ]");
            return false;
        }
        $comment = DB::getDoctrineSchemaManager()->listTableDetails($slug)->getComment();
        if (!$comment) {
            $this->error("数据库表 [ {$slug} ] 没有备注");
            return false;
        }
        $requiredFields = array_unique(array_merge(
            SystemModel::$searchHideField,
            SystemModel::$listReturnField,
            SystemModel::$formHideField,
            SystemModel::$detailHideField
        ));
        // 判断数据表是否包含必要字段
        foreach ($requiredFields as $requiredField) {
            if (!Schema::hasColumn($slug, $requiredField)) {
                $this->error("数据库表 [ {$slug} ] 没有 [ {$requiredField} ] 字段，请创建");
                return false;
            }
        }
        $this->title = str_replace('表', '', $comment);
        $this->name = underlineToBar(Str::singular(Str::kebab($this->argument('slug'))));
        Artisan::call("admin:make-model ".$slug.' --force');
        try {
            Artisan::call("database:sync");
        } catch (\Throwable $e) {
            Log::error($e->getMessage());
        }
        DB::beginTransaction();
        try {
            $this->makeBackend();// 创建后端文件
            $this->makeBackRoute();// 创建后端路由
            $this->makeAdminPermission();// 创建后台权限
            $this->makeAdminMenu();// 创建后台菜单
            $this->makeFrontRoute();// 创建前端路由
            $this->makeFrontend();// 创建前端文件
            DB::commit();
            return true;
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            DB::rollBack();  
            return false;
        }
    }

    protected function makeBackend()
    {
        foreach ($this->types as $type) {
            $this->nowType = $type;
            $this->type = Str::ucfirst($type);

            if (($type == 'model') && ($model = $this->option('model'))) {
                if (!class_exists($model)) {
                    $this->error("模型 [ {$model} ] 不存在");
                    return false;
                } elseif (class_exists($model)) {
                    $this->classes[$type] = trim($model, '\\');
                    continue;
                }
            }

            if (($type == 'test') && (!$this->option('test'))) {
                continue;
            }

            if (parent::handle() === false) {
                continue;
            }
            $this->classes[$type] = $this->qualifyClass($this->getNameInput());
        }
    }

    protected function getStub()
    {
        return __DIR__."/stubs/{$this->nowType}.stub";
    }

    protected function getNameInput()
    {
        $name = Str::studly(trim($this->name));

        if ($this->nowType == 'test') {
            $name = 'Feature\\'.$name.'ControllerTest';
        } elseif ($this->nowType != 'model') {
            $name .= $this->type;
        }

        return $name;
    }

    protected function rootNamespace()
    {
        switch ($this->nowType) {
            case 'test':
                return 'Tests\\Admin';
                break;
            
            default:
                return 'App\\Admin\\'.Str::ucfirst(Str::plural($this->nowType));
                break;
        }
    }

    protected function getPath($name)
    {
        $name = Str::replaceFirst($this->rootNamespace(), '', $name);
        $path = $this->laravel['path'].'/';
        switch ($this->nowType) {
            case 'test':
                $path = $this->laravel['path.base'].'/tests';
                break;
            
            default:
                $path .= 'Admin/'.
                    Str::ucfirst(Str::plural($this->type));
                break;
        }
        
        return $path.str_replace('\\', '/', $name).'.php';
    }

    protected function replaceClass($stub, $name)
    {
        $stub = parent::replaceClass($stub, $name);

        if ($this->nowType == 'test') {
            $stub = str_replace('NamespacedDummyModel', $this->classes['model'], $stub);
            $stub = str_replace('dummy-resource-name', Str::plural($this->name), $stub);
        } elseif ($this->nowType == 'controller' || $this->nowType == 'showdoc') {
            foreach (['filter', 'request', 'resource', 'model'] as $type) {
                $stub = $this->replaceDummyResource($type, $stub);
            }
        } elseif ($this->nowType == 'filter' || $this->nowType == 'request') {
            $stub = $this->replaceDummyResource('model', $stub);
        } 
        return $stub;
    }

    protected function replaceDummyResource(string $type, string $stub)
    {
        
        $name = trim($this->name);
        $title = trim($this->title);
        $singularDummyResource = Str::singular(barToUnderline($name));
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));
        $param = SystemModel::formatParam($pluralKebabDummyResource);
        $returnParam = SystemModel::formatReturnParam($pluralKebabDummyResource);
        $jsonParam = SystemModel::formatJsonParam($pluralKebabDummyResource);
        $return = str_replace('@json_param', '@return', $jsonParam);
        $namespaced = $this->classes[$type];
        $class = class_basename($namespaced);
        $type = Str::ucfirst($type);
        $stub = str_replace("NamespacedDummy{$type}", $namespaced, $stub);
        $stub = str_replace("Dummy{$type}", $class, $stub);
        $stub = str_replace("DummyTitle", $title, $stub);
        $stub = str_replace("dummy-resources", $pluralKebabDummyResource, $stub);
        $stub = str_replace("dummy_resource", $singularDummyResource, $stub);
        $stub = str_replace("showDummyDoc", "showdoc", $stub);

        $stub = str_replace("* store@json_param {}", $jsonParam, $stub);
        $stub = str_replace("* store@param 参数名 是否必选 类型 说明", $param, $stub);
        $stub = str_replace("* store@return {}", $return, $stub);
        $stub = str_replace("* store@return_param 参数名 类型 说明", $returnParam, $stub);
        

        $stub = str_replace("* update@json_param {}", $jsonParam, $stub);
        $stub = str_replace("* update@param 参数名 是否必选 类型 说明", $param, $stub);
        $stub = str_replace("* update@return {}", $return, $stub);
        $stub = str_replace("* update@return_param 参数名 类型 说明", $returnParam, $stub);

        $stub = str_replace("* destory", "* ", $stub);

        $stub = str_replace("* index@param 参数名 是否必选 类型 说明", $param, $stub);
        $stub = str_replace("* index@return {}", $return, $stub);
        $stub = str_replace("* index@return_param 参数名 类型 说明", $returnParam, $stub);

        $stub = str_replace("* show@param 参数名 是否必选 类型 说明", '* @param 参数名 是否必选 类型 说明', $stub);
        $stub = str_replace("* show@return {}", $return, $stub);
        $stub = str_replace("* show@return_param 参数名 类型 说明", $returnParam, $stub);

        if ($type == 'Model') {
            $model = '$'.Str::camel($class);
            $models = Str::plural($model);

            $stub = str_replace('$dummyModel', $model, $stub);
            $stub = str_replace('$dummyModels', $models, $stub);
        }

        return $stub;
    }

    /**
     * 创建后端resource资源文件
     */
    protected function makeFrontend()
    {
        $name = trim($this->name);

        $dummyResource = Str::camel($name);
        $ucDummyResource = Str::ucfirst($dummyResource);
        $pluralDummyResource = Str::plural($dummyResource);
        $ucPluralDummyResource = Str::ucfirst($pluralDummyResource);
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));

        $replaces = [
            'PluralDummyResource' => $ucPluralDummyResource,
            'dummy-resources' => $pluralKebabDummyResource,
            'DummyResource' => $ucDummyResource,
            'dummyResources' => $pluralDummyResource,
        ];

        foreach (['api', 'index', 'form', 'detail'] as $type) {
            $content = $this->files->get(__DIR__."/stubs/frontend/{$type}.stub");
            foreach ($replaces as $search => $replace) {
                $content = str_replace($search, $replace, $content);
            }

            $relativePath = str_replace('dummy-resources', $pluralKebabDummyResource, $this->frontendTypePathMap[$type]);
            $path = $this->laravel['path.resources'].'/admin/src/'.$relativePath;

            if (
                !$this->option('force') &&
                $this->files->exists($path)
            ) {
                $this->warn("[ {$relativePath} ] 已存在");
                continue;
            }

            $this->makeDirectory($path);
            $this->files->put($path, $content);

            $this->info("[ {$relativePath} ] 创建成功");
        }
    }

    /**
     * 创建vue前端路由
     */
    protected function makeFrontRoute()
    {
        $name = trim($this->name);
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));
        $path = "resources/admin/src/views/pages.js";
        $routeTemplate = <<<EOT
          \n
          'dummy-resources': () => import('@v/dummy-resources/Index'),
          'dummy-resources/create': () => import('@v/dummy-resources/Form'),
          'dummy-resources/:id(\\\\d+)/edit': () => import('@v/dummy-resources/Form'),
          'dummy-resources/:id(\\\\d+)': () => import('@v/dummy-resources/Detail'),
        EOT;
        $search = "\n}";
        $replace = str_replace('dummy-resources', $pluralKebabDummyResource, $routeTemplate).$search;
        $content = $this->files->get($path);
        $contentArr = explode("\n", $content);
        $replaceArr = explode("\n", $replace);
        foreach ($contentArr as $key => $value) {
            for ($i=2; $i <= 5; $i++) { 
                if (isset($replaceArr[$i]) && strstr($value, $replaceArr[$i])) {
                    unset($replaceArr[$i]);
                }
            }
        }
        if (count($replaceArr) < 4) {
            $this->warn('前端路由已存在');
        } else {
            $replace = implode("\n", $replaceArr);
            $content = str_replace($search, $replace, $content);
            $this->files->put($path, $content);
            $this->info('新增前端路由：');
            $this->line($replace);
            $this->info('前端路由创建成功');
        }
    }

    /**
     * 创建laravel后端路由
     */
    protected function makeBackRoute()
    {
        $name = trim($this->name);
        $dummyResource = Str::camel($name);
        $ucDummyResource = Str::ucfirst($dummyResource);
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));
        $controller = "C\\".$ucDummyResource."Controller::class";
        $path = "routes/admin.php";
        $search = "\n});});";
        $replace = PHP_EOL."Route::resource('{$pluralKebabDummyResource}', $controller);".$search;
        $content = $this->files->get($path);
        $contentArr = explode("\n", $content);
        $replaceArr = explode("\n", $replace);
        foreach ($contentArr as $key => $value) {
            if (isset($replaceArr[1]) && strstr($value, $replaceArr[1])) {
                unset($replaceArr[1]);
            }
        }
        if (count($replaceArr) < 3) {
            $this->warn('后端路由已存在');
        } else {
            $replace = implode("\n", $replaceArr);
            $content = str_replace($search, $replace, $content);
            $this->files->put($path, $content);
            $this->info('新增后端路由：');
            $this->line($replace);
            $this->info('后端路由创建成功');
        }
    }

    /**
     * 创建后台菜单
     */
    protected function makeAdminMenu(){
        $name = trim($this->name);
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));
        $title = trim($this->title);
        $vueRouter = app(VueRouter::class);
        $parent_id = $vueRouter->where('title', $title.'管理')->value('id');
        if (!$parent_id){
            $vueRouter->insert([
                'parent_id'=>0,
                'title'=>$title.'管理',
                'order'=>2,
                'menu'=>1,
                'permission'=>'pass-get-'.$pluralKebabDummyResource
            ]);
            $parent_id = DB::getPdo()->lastInsertId();
        }
        if ($parent_id) {
            $servantMenuList = [
                [
                    'parent_id'=>$parent_id,
                    'title'=>$title.'列表',
                    'path'=>$pluralKebabDummyResource,
                    'order'=>0,
                    'menu'=>1,
                    'permission'=>'pass-get-'.$pluralKebabDummyResource
                ],
                [
                    'parent_id'=>$parent_id,
                    'title'=>'添加'.$title,
                    'path'=>$pluralKebabDummyResource.'/create',
                    'order'=>1,
                    'menu'=>1,
                    'permission'=>'pass-post-'.$pluralKebabDummyResource
                ],
                [
                    'parent_id'=>$parent_id,
                    'title'=>'编辑'.$title,
                    'path'=>$pluralKebabDummyResource.'/:id(\d+)/edit',
                    'order'=>2,
                    'menu'=>0,
                    'permission'=>'pass-put-'.$pluralKebabDummyResource
                ],
                [
                    'parent_id'=>$parent_id,
                    'title'=>$title.'详情',
                    'path'=>$pluralKebabDummyResource.'/:id(\d+)',
                    'order'=>3,
                    'menu'=>0,
                    'permission'=>'pass-get-'.$pluralKebabDummyResource
                ],
            ];
            foreach ($servantMenuList as $key => $value) {
                if ($vueRouter->where('path', $value['path'])->first()){
                    $this->warn("菜单 [ {$value['title']} ] 已存在");
                    unset($servantMenuList[$key]);
                    continue;
                } else {
                    if ($vueRouter->insert($value)) {
                        $this->info("菜单 [ {$value['title']} ] 创建成功");
                    }
                }
            }
            $this->makeAdminUserMenu($vueRouter->toTree());
        }
    }

    /**
     * 创建后台非管理员用户路由菜单
     */
    protected function makeAdminUserMenu($vueRouterList){
        $vueAdminUserRouter = app(VueAdminUserRouter::class);
        $adminUserIdList = AdminUser::where('id','<>',1)->pluck('id');
        foreach ($adminUserIdList as $key => $value) {
            $vueAdminUserRouter->createVueAdminUserRouter($vueRouterList, $value);
        }
    }

    /**
     * 创建后台权限
     */
    protected function makeAdminPermission()
    {
        $name = trim($this->name);
        $pluralKebabDummyResource = Str::plural(Str::kebab($name));
        $title = trim($this->title);
        $adminPermissionList = [
            [
                'name'=>'新增'.$title.'权限',
                'slug'=>'pass-post-'.$pluralKebabDummyResource,
                'http_method'=>null,
                'http_path'=>"POST:/{$pluralKebabDummyResource}\nGET:/{$pluralKebabDummyResource}/create"
            ],
            [
                'name'=>'删除'.$title.'权限',
                'slug'=>'pass-delete-'.$pluralKebabDummyResource,
                'http_method'=>'DELETE',
                'http_path'=>'/'.$pluralKebabDummyResource.'*'
            ],
            [
                'name'=>'修改'.$title.'权限',
                'slug'=>'pass-put-'.$pluralKebabDummyResource,
                'http_method'=>'PUT',
                'http_path'=>'/'.$pluralKebabDummyResource.'*'
            ],
            [
                'name'=>'查看'.$title.'权限',
                'slug'=>'pass-get-'.$pluralKebabDummyResource,
                'http_method'=>'GET',
                'http_path'=>'/'.$pluralKebabDummyResource.'*'
            ],
        ];
        $adminPermission = app(AdminPermission::class);
        foreach ($adminPermissionList as $key => $value) {
            if (!$adminPermission->existAdminPermission($value['slug'])) {
                $this->warn("权限 [ {$value['name']} ] 已存在");
                continue;
            } 
            if ($adminPermission->create($value)) {
                $this->info("权限 [ {$value['name']} ] 创建成功");
            } else {
                $this->error("权限 [ {$value['name']} ] 创建失败");
            }
        }
    }
}